/*
 * Site.java
 *
 * Created on Jan 2, 2011, 9:44:13 PM
 *
 * @author pquiring
 */

import java.awt.event.*;
import java.io.*;
import java.awt.*;
import java.util.*;
import java.text.*;
import javax.swing.*;
import javax.swing.tree.*;
import javax.swing.table.*;

import javaforce.*;

public abstract class Site extends javax.swing.JPanel implements FTP.ProgressListener {

  /**
   * Creates new form Site
   */
  public Site() {
    initComponents();
  }

  /**
   * This method is called from within the constructor to initialize the form.
   * WARNING: Do NOT modify this code. The content of this method is always
   * regenerated by the Form Editor.
   */
  @SuppressWarnings("unchecked")
  // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
  private void initComponents() {

    localMenu = new javax.swing.JPopupMenu();
    local_upload = new javax.swing.JMenuItem();
    jSeparator1 = new javax.swing.JPopupMenu.Separator();
    local_mkdir = new javax.swing.JMenuItem();
    local_refresh = new javax.swing.JMenuItem();
    jSeparator2 = new javax.swing.JPopupMenu.Separator();
    local_delete = new javax.swing.JMenuItem();
    local_rename = new javax.swing.JMenuItem();
    remoteMenu = new javax.swing.JPopupMenu();
    remote_download = new javax.swing.JMenuItem();
    jSeparator4 = new javax.swing.JPopupMenu.Separator();
    remote_mkdir = new javax.swing.JMenuItem();
    remote_refresh = new javax.swing.JMenuItem();
    jSeparator5 = new javax.swing.JPopupMenu.Separator();
    remote_delete = new javax.swing.JMenuItem();
    remote_rename = new javax.swing.JMenuItem();
    jSeparator6 = new javax.swing.JPopupMenu.Separator();
    remote_props = new javax.swing.JMenuItem();
    split_main = new javax.swing.JSplitPane();
    split_files = new javax.swing.JSplitPane();
    localSide = new javax.swing.JPanel();
    jLabel2 = new javax.swing.JLabel();
    localDir = new javax.swing.JTextField();
    split_local = new javax.swing.JSplitPane();
    jScrollPane2 = new javax.swing.JScrollPane();
    localTree = new javax.swing.JTree();
    jScrollPane4 = new javax.swing.JScrollPane();
    localFiles = new javax.swing.JTable();
    remoteSide = new javax.swing.JPanel();
    jLabel1 = new javax.swing.JLabel();
    remoteDir = new javax.swing.JTextField();
    split_remote = new javax.swing.JSplitPane();
    jScrollPane1 = new javax.swing.JScrollPane();
    remoteTree = new javax.swing.JTree();
    jScrollPane3 = new javax.swing.JScrollPane();
    remoteFiles = new javax.swing.JTable();
    status_pane = new javax.swing.JTabbedPane();
    jPanel1 = new javax.swing.JPanel();
    progress = new javax.swing.JProgressBar();
    status = new javax.swing.JLabel();
    abort = new javax.swing.JButton();
    jPanel2 = new javax.swing.JPanel();
    jScrollPane5 = new javax.swing.JScrollPane();
    log = new javax.swing.JTextArea();

    local_upload.setText("Upload");
    local_upload.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(java.awt.event.ActionEvent evt) {
        local_uploadActionPerformed(evt);
      }
    });
    localMenu.add(local_upload);
    localMenu.add(jSeparator1);

    local_mkdir.setText("Create Directory");
    local_mkdir.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(java.awt.event.ActionEvent evt) {
        local_mkdirActionPerformed(evt);
      }
    });
    localMenu.add(local_mkdir);

    local_refresh.setText("Refresh");
    local_refresh.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(java.awt.event.ActionEvent evt) {
        local_refreshActionPerformed(evt);
      }
    });
    localMenu.add(local_refresh);
    localMenu.add(jSeparator2);

    local_delete.setText("Delete");
    local_delete.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(java.awt.event.ActionEvent evt) {
        local_deleteActionPerformed(evt);
      }
    });
    localMenu.add(local_delete);

    local_rename.setText("Rename");
    local_rename.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(java.awt.event.ActionEvent evt) {
        local_renameActionPerformed(evt);
      }
    });
    localMenu.add(local_rename);

    remote_download.setText("Download");
    remote_download.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(java.awt.event.ActionEvent evt) {
        remote_downloadActionPerformed(evt);
      }
    });
    remoteMenu.add(remote_download);
    remoteMenu.add(jSeparator4);

    remote_mkdir.setText("Create Directory");
    remote_mkdir.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(java.awt.event.ActionEvent evt) {
        remote_mkdirActionPerformed(evt);
      }
    });
    remoteMenu.add(remote_mkdir);

    remote_refresh.setText("Refresh");
    remote_refresh.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(java.awt.event.ActionEvent evt) {
        remote_refreshActionPerformed(evt);
      }
    });
    remoteMenu.add(remote_refresh);
    remoteMenu.add(jSeparator5);

    remote_delete.setText("Delete");
    remote_delete.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(java.awt.event.ActionEvent evt) {
        remote_deleteActionPerformed(evt);
      }
    });
    remoteMenu.add(remote_delete);

    remote_rename.setText("Rename");
    remote_rename.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(java.awt.event.ActionEvent evt) {
        remote_renameActionPerformed(evt);
      }
    });
    remoteMenu.add(remote_rename);
    remoteMenu.add(jSeparator6);

    remote_props.setText("Properties");
    remote_props.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(java.awt.event.ActionEvent evt) {
        remote_propsActionPerformed(evt);
      }
    });
    remoteMenu.add(remote_props);

    addComponentListener(new java.awt.event.ComponentAdapter() {
      public void componentResized(java.awt.event.ComponentEvent evt) {
        formComponentResized(evt);
      }
      public void componentShown(java.awt.event.ComponentEvent evt) {
        formComponentShown(evt);
      }
    });

    split_main.setDividerLocation(400);
    split_main.setOrientation(javax.swing.JSplitPane.VERTICAL_SPLIT);

    split_files.setDividerLocation(300);

    localSide.setBorder(javax.swing.BorderFactory.createBevelBorder(javax.swing.border.BevelBorder.RAISED));

    jLabel2.setText("Local site:");

    localDir.setEditable(false);
    localDir.setText("/");

    split_local.setDividerLocation(200);
    split_local.setOrientation(javax.swing.JSplitPane.VERTICAL_SPLIT);

    javax.swing.tree.DefaultMutableTreeNode treeNode1 = new javax.swing.tree.DefaultMutableTreeNode("root");
    localTree.setModel(new javax.swing.tree.DefaultTreeModel(treeNode1));
    localTree.setComponentPopupMenu(localMenu);
    localTree.addMouseListener(new java.awt.event.MouseAdapter() {
      public void mouseClicked(java.awt.event.MouseEvent evt) {
        localTreeMouseClicked(evt);
      }
    });
    jScrollPane2.setViewportView(localTree);

    split_local.setTopComponent(jScrollPane2);

    jScrollPane4.setComponentPopupMenu(localMenu);

    localFiles.setModel(new javax.swing.table.DefaultTableModel(
      new Object [][] {
        {null, null, null, null},
        {null, null, null, null},
        {null, null, null, null},
        {null, null, null, null}
      },
      new String [] {
        "Title 1", "Title 2", "Title 3", "Title 4"
      }
    ));
    localFiles.setComponentPopupMenu(localMenu);
    localFiles.addMouseListener(new java.awt.event.MouseAdapter() {
      public void mouseClicked(java.awt.event.MouseEvent evt) {
        localFilesMouseClicked(evt);
      }
    });
    localFiles.addKeyListener(new java.awt.event.KeyAdapter() {
      public void keyPressed(java.awt.event.KeyEvent evt) {
        localFilesKeyPressed(evt);
      }
    });
    jScrollPane4.setViewportView(localFiles);

    split_local.setRightComponent(jScrollPane4);

    javax.swing.GroupLayout localSideLayout = new javax.swing.GroupLayout(localSide);
    localSide.setLayout(localSideLayout);
    localSideLayout.setHorizontalGroup(
      localSideLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
      .addGroup(localSideLayout.createSequentialGroup()
        .addGap(10, 10, 10)
        .addComponent(jLabel2)
        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
        .addComponent(localDir, javax.swing.GroupLayout.DEFAULT_SIZE, 227, Short.MAX_VALUE))
      .addComponent(split_local, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, 295, Short.MAX_VALUE)
    );
    localSideLayout.setVerticalGroup(
      localSideLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
      .addGroup(localSideLayout.createSequentialGroup()
        .addGroup(localSideLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
          .addComponent(localDir, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
          .addComponent(jLabel2))
        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
        .addComponent(split_local, javax.swing.GroupLayout.DEFAULT_SIZE, 270, Short.MAX_VALUE))
    );

    split_files.setLeftComponent(localSide);

    remoteSide.setBorder(javax.swing.BorderFactory.createBevelBorder(javax.swing.border.BevelBorder.RAISED));

    jLabel1.setText("Remote site:");

    remoteDir.setEditable(false);
    remoteDir.setText("/");

    split_remote.setDividerLocation(200);
    split_remote.setOrientation(javax.swing.JSplitPane.VERTICAL_SPLIT);

    treeNode1 = new javax.swing.tree.DefaultMutableTreeNode("root");
    remoteTree.setModel(new javax.swing.tree.DefaultTreeModel(treeNode1));
    remoteTree.setComponentPopupMenu(remoteMenu);
    remoteTree.addMouseListener(new java.awt.event.MouseAdapter() {
      public void mouseClicked(java.awt.event.MouseEvent evt) {
        remoteTreeMouseClicked(evt);
      }
    });
    jScrollPane1.setViewportView(remoteTree);

    split_remote.setTopComponent(jScrollPane1);

    jScrollPane3.setComponentPopupMenu(remoteMenu);

    remoteFiles.setModel(new javax.swing.table.DefaultTableModel(
      new Object [][] {
        {null, null, null, null},
        {null, null, null, null},
        {null, null, null, null},
        {null, null, null, null}
      },
      new String [] {
        "Title 1", "Title 2", "Title 3", "Title 4"
      }
    ));
    remoteFiles.setComponentPopupMenu(remoteMenu);
    remoteFiles.addMouseListener(new java.awt.event.MouseAdapter() {
      public void mouseClicked(java.awt.event.MouseEvent evt) {
        remoteFilesMouseClicked(evt);
      }
    });
    remoteFiles.addKeyListener(new java.awt.event.KeyAdapter() {
      public void keyPressed(java.awt.event.KeyEvent evt) {
        remoteFilesKeyPressed(evt);
      }
    });
    jScrollPane3.setViewportView(remoteFiles);

    split_remote.setRightComponent(jScrollPane3);

    javax.swing.GroupLayout remoteSideLayout = new javax.swing.GroupLayout(remoteSide);
    remoteSide.setLayout(remoteSideLayout);
    remoteSideLayout.setHorizontalGroup(
      remoteSideLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
      .addGroup(remoteSideLayout.createSequentialGroup()
        .addGap(10, 10, 10)
        .addComponent(jLabel1)
        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
        .addComponent(remoteDir, javax.swing.GroupLayout.DEFAULT_SIZE, 273, Short.MAX_VALUE))
      .addComponent(split_remote, javax.swing.GroupLayout.DEFAULT_SIZE, 348, Short.MAX_VALUE)
    );
    remoteSideLayout.setVerticalGroup(
      remoteSideLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
      .addGroup(remoteSideLayout.createSequentialGroup()
        .addGroup(remoteSideLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
          .addComponent(remoteDir, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
          .addComponent(jLabel1))
        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
        .addComponent(split_remote, javax.swing.GroupLayout.DEFAULT_SIZE, 270, Short.MAX_VALUE))
    );

    split_files.setRightComponent(remoteSide);

    split_main.setLeftComponent(split_files);

    status.setText("Status...");

    abort.setText("Abort");
    abort.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(java.awt.event.ActionEvent evt) {
        abortActionPerformed(evt);
      }
    });

    javax.swing.GroupLayout jPanel1Layout = new javax.swing.GroupLayout(jPanel1);
    jPanel1.setLayout(jPanel1Layout);
    jPanel1Layout.setHorizontalGroup(
      jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
      .addGroup(jPanel1Layout.createSequentialGroup()
        .addContainerGap()
        .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
          .addComponent(status, javax.swing.GroupLayout.DEFAULT_SIZE, 633, Short.MAX_VALUE)
          .addComponent(progress, javax.swing.GroupLayout.DEFAULT_SIZE, 633, Short.MAX_VALUE)
          .addComponent(abort, javax.swing.GroupLayout.Alignment.TRAILING))
        .addContainerGap())
    );
    jPanel1Layout.setVerticalGroup(
      jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
      .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, jPanel1Layout.createSequentialGroup()
        .addContainerGap()
        .addComponent(status)
        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
        .addComponent(progress, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
        .addComponent(abort)
        .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
    );

    status_pane.addTab("File Transfer", jPanel1);

    log.setColumns(20);
    log.setEditable(false);
    log.setRows(5);
    jScrollPane5.setViewportView(log);

    javax.swing.GroupLayout jPanel2Layout = new javax.swing.GroupLayout(jPanel2);
    jPanel2.setLayout(jPanel2Layout);
    jPanel2Layout.setHorizontalGroup(
      jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
      .addGroup(jPanel2Layout.createSequentialGroup()
        .addContainerGap()
        .addComponent(jScrollPane5, javax.swing.GroupLayout.DEFAULT_SIZE, 633, Short.MAX_VALUE)
        .addContainerGap())
    );
    jPanel2Layout.setVerticalGroup(
      jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
      .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, jPanel2Layout.createSequentialGroup()
        .addContainerGap()
        .addComponent(jScrollPane5, javax.swing.GroupLayout.DEFAULT_SIZE, 79, Short.MAX_VALUE)
        .addContainerGap())
    );

    status_pane.addTab("Transfer Log", jPanel2);

    split_main.setRightComponent(status_pane);

    javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
    this.setLayout(layout);
    layout.setHorizontalGroup(
      layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
      .addComponent(split_main)
    );
    layout.setVerticalGroup(
      layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
      .addComponent(split_main)
    );
  }// </editor-fold>//GEN-END:initComponents

    private void remote_downloadActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_remote_downloadActionPerformed
      download();
    }//GEN-LAST:event_remote_downloadActionPerformed

    private void remoteFilesMouseClicked(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_remoteFilesMouseClicked
      if (evt.getClickCount() != 2) {
        return;
      }
      if (evt.getButton() != 1) {
        return;
      }
      dblclickRemote();
    }//GEN-LAST:event_remoteFilesMouseClicked

    private void localFilesMouseClicked(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_localFilesMouseClicked
      if (evt.getClickCount() != 2) {
        return;
      }
      if (evt.getButton() != 1) {
        return;
      }
      dblclickLocal();
    }//GEN-LAST:event_localFilesMouseClicked

    private void formComponentResized(java.awt.event.ComponentEvent evt) {//GEN-FIRST:event_formComponentResized
      resize();
    }//GEN-LAST:event_formComponentResized

    private void formComponentShown(java.awt.event.ComponentEvent evt) {//GEN-FIRST:event_formComponentShown
      resize();
    }//GEN-LAST:event_formComponentShown

    private void local_uploadActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_local_uploadActionPerformed
      upload();
    }//GEN-LAST:event_local_uploadActionPerformed

    private void local_mkdirActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_local_mkdirActionPerformed
      local_mkdir();
    }//GEN-LAST:event_local_mkdirActionPerformed

    private void local_refreshActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_local_refreshActionPerformed
      parseLocal();
    }//GEN-LAST:event_local_refreshActionPerformed

    private void local_deleteActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_local_deleteActionPerformed
      local_delete();
    }//GEN-LAST:event_local_deleteActionPerformed

    private void local_renameActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_local_renameActionPerformed
      local_rename();
    }//GEN-LAST:event_local_renameActionPerformed

    private void remote_mkdirActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_remote_mkdirActionPerformed
      remote_mkdir();
    }//GEN-LAST:event_remote_mkdirActionPerformed

    private void remote_refreshActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_remote_refreshActionPerformed
      remote_chdir(".");
    }//GEN-LAST:event_remote_refreshActionPerformed

    private void remote_deleteActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_remote_deleteActionPerformed
      remote_delete();
    }//GEN-LAST:event_remote_deleteActionPerformed

    private void remote_renameActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_remote_renameActionPerformed
      remote_rename();
    }//GEN-LAST:event_remote_renameActionPerformed

    private void remote_propsActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_remote_propsActionPerformed
      remote_props();
    }//GEN-LAST:event_remote_propsActionPerformed

    private void remoteTreeMouseClicked(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_remoteTreeMouseClicked
      if (evt.getClickCount() != 2) {
        return;
      }
      if (evt.getButton() != 1) {
        return;
      }
      dblclickRemoteTree();
    }//GEN-LAST:event_remoteTreeMouseClicked

    private void localTreeMouseClicked(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_localTreeMouseClicked
      if (evt.getClickCount() != 2) {
        return;
      }
      if (evt.getButton() != 1) {
        return;
      }
      dblclickLocalTree();
    }//GEN-LAST:event_localTreeMouseClicked

  private void localFilesKeyPressed(java.awt.event.KeyEvent evt) {//GEN-FIRST:event_localFilesKeyPressed
    int keycode = evt.getKeyCode();
    int mods = evt.getModifiers();
    if (mods == KeyEvent.CTRL_MASK) {
      switch (keycode) {
        case KeyEvent.VK_C:
//          local_copy();
          break;
        case KeyEvent.VK_X:
//          local_cut();
          break;
        case KeyEvent.VK_V:
//          local_paste();
          break;
      }
    }
    if (mods == 0) {
      if (keycode == KeyEvent.VK_DELETE) {
        local_delete();
      }
    }
  }//GEN-LAST:event_localFilesKeyPressed

  private void remoteFilesKeyPressed(java.awt.event.KeyEvent evt) {//GEN-FIRST:event_remoteFilesKeyPressed
    int keycode = evt.getKeyCode();
    int mods = evt.getModifiers();
    if (mods == KeyEvent.CTRL_MASK) {
      switch (keycode) {
        case KeyEvent.VK_C:
//          remote_copy();
          break;
        case KeyEvent.VK_X:
//          remote_cut();
          break;
        case KeyEvent.VK_V:
//          remote_paste();
          break;
      }
    }
    if (mods == 0) {
      if (keycode == KeyEvent.VK_DELETE) {
        remote_delete();
      }
    }
  }//GEN-LAST:event_remoteFilesKeyPressed

  private void abortActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_abortActionPerformed
    abort();
  }//GEN-LAST:event_abortActionPerformed


  // Variables declaration - do not modify//GEN-BEGIN:variables
  private javax.swing.JButton abort;
  private javax.swing.JLabel jLabel1;
  private javax.swing.JLabel jLabel2;
  private javax.swing.JPanel jPanel1;
  private javax.swing.JPanel jPanel2;
  private javax.swing.JScrollPane jScrollPane1;
  private javax.swing.JScrollPane jScrollPane2;
  private javax.swing.JScrollPane jScrollPane3;
  private javax.swing.JScrollPane jScrollPane4;
  private javax.swing.JScrollPane jScrollPane5;
  private javax.swing.JPopupMenu.Separator jSeparator1;
  private javax.swing.JPopupMenu.Separator jSeparator2;
  private javax.swing.JPopupMenu.Separator jSeparator4;
  private javax.swing.JPopupMenu.Separator jSeparator5;
  private javax.swing.JPopupMenu.Separator jSeparator6;
  public javax.swing.JTextField localDir;
  public javax.swing.JTable localFiles;
  private javax.swing.JPopupMenu localMenu;
  private javax.swing.JPanel localSide;
  private javax.swing.JTree localTree;
  private javax.swing.JMenuItem local_delete;
  private javax.swing.JMenuItem local_mkdir;
  private javax.swing.JMenuItem local_refresh;
  private javax.swing.JMenuItem local_rename;
  private javax.swing.JMenuItem local_upload;
  private javax.swing.JTextArea log;
  public javax.swing.JProgressBar progress;
  public javax.swing.JTextField remoteDir;
  public javax.swing.JTable remoteFiles;
  private javax.swing.JPopupMenu remoteMenu;
  private javax.swing.JPanel remoteSide;
  private javax.swing.JTree remoteTree;
  private javax.swing.JMenuItem remote_delete;
  private javax.swing.JMenuItem remote_download;
  private javax.swing.JMenuItem remote_mkdir;
  private javax.swing.JMenuItem remote_props;
  private javax.swing.JMenuItem remote_refresh;
  private javax.swing.JMenuItem remote_rename;
  private javax.swing.JSplitPane split_files;
  private javax.swing.JSplitPane split_local;
  private javax.swing.JSplitPane split_main;
  private javax.swing.JSplitPane split_remote;
  public javax.swing.JLabel status;
  private javax.swing.JTabbedPane status_pane;
  // End of variables declaration//GEN-END:variables

  public XML localFolders = new XML();
  public XML remoteFolders = new XML();
  public int totalFileSize = 0;
  private static ImageIcon ii[] = new ImageIcon[24];
  private static final int PIC_FOLDER = 0;
  private static final int PIC_LINK = 1;
  private static final int PIC_FILE = 2;
  private static String icons[] = {
    "folder.png", "link.png", "file.png"
  };
  public DefaultTableModel localFilesTableModel = new DefaultTableModel();
  public DefaultTableModel remoteFilesTableModel = new DefaultTableModel();
  private DefaultTableColumnModel localFilesTableColumnModel = new DefaultTableColumnModel();
  private DefaultTableColumnModel remoteFilesTableColumnModel = new DefaultTableColumnModel();
  public boolean aborted = false;

  public abstract boolean connect(SiteDetails sd);

  public abstract void disconnect();

  public abstract void remote_chdir(String path);

  public abstract void remote_mkdir();

  public abstract void remote_delete();

  public abstract void remote_rename();

  public abstract void remote_props();

  public abstract void download();

  public abstract void upload();

  public abstract void abort();
  public ArrayList<String> localFolderStack;  //used for moving in/out folders (in case a folder is a sym-link)
  public ArrayList<String> remoteFolderStack;  //used for moving in/out folders (in case a folder is a sym-link)

  private TableCellRenderer tableCellRenderer = new TableCellRenderer() {
    public Component getTableCellRendererComponent(JTable table,
      Object value,
      boolean isSelected,
      boolean hasFocus,
      int row,
      int column) {
      //each Object 'is' a JLabel (Contact) which makes this easy
      if (value == null) {
        return new JLabel("null");
      }
      JLabel label;
      if (value instanceof String) {
        label = new JLabel((String) value);
      } else {
        label = (JLabel) value;
      }
      if (isSelected) {
        label.setBackground(table.getSelectionBackground());
        label.setForeground(table.getSelectionForeground());
      } else {
        label.setBackground(table.getBackground());
        label.setForeground(table.getForeground());
      }
      label.setEnabled(table.isEnabled());
      label.setFont(table.getFont());
      label.setOpaque(true);
      return label;
    }
  };

  private TableCellEditor tableCellEditor = new DefaultCellEditor(new JTextField("")) {
    @Override
    public boolean isCellEditable(EventObject anEvent) {
      return false;  //BUG : inline editing doesn't work yet
    }

    @Override
    public Component getTableCellEditorComponent(JTable table,
      Object value,
      boolean isSelected,
      int row,
      int column) {
      String str = "";
      if (value instanceof JLabel) {
        str = ((JLabel) value).getText();
      }
      if (value instanceof String) {
        str = (String) value;
      }
      return new JTextField(str);
    }
  };

  protected static class SiteFile extends javax.swing.JLabel implements Comparable {

    public SiteFile(String label, int filesize, int type, String link) {
      super(label);
      this.filesize = filesize;
      setIcon(ii[type]);
      switch (type) {
        case PIC_FOLDER:
          isDir = true;
          break;
        case PIC_LINK:
          isLink = true;
          if (link.endsWith("/")) {
            isDir = true;
          }
          break;
      }
      this.link = link;
    }
    public int filesize;
    public boolean isDir = false;
    public boolean isLink = false;
    public String link = "";

    public int compareTo(Object o) {
      SiteFile sf = (SiteFile) o;
      //place folders first
      if (sf.isDir && !isDir) {
        return 1;
      }
      if (!sf.isDir && isDir) {
        return -1;
      }
      return getText().compareTo(sf.getText());
    }
  }

  private static class LocalFileSorter implements Comparable {

    public LocalFileSorter(File file) {
      this.file = file;
    }
    public File file;

    public int compareTo(Object o) {
      LocalFileSorter that = (LocalFileSorter) o;
      //place folders first
      if (that.file.isDirectory() && !file.isDirectory()) {
        return 1;
      }
      if (!that.file.isDirectory() && file.isDirectory()) {
        return -1;
      }
      return file.getName().compareTo(that.file.getName());
    }
  }

  private static class RemoteFileSorter implements Comparable {

    public RemoteFileSorter(String fs[]) {
      this.fs = fs;
    }
    public String fs[];

    public boolean isDirectory() {
      return fs[0].charAt(0) == 'd';
    }

    public boolean isLink() {
      return fs[0].charAt(0) == 'l';
    }

    public int compareTo(Object o) {
      RemoteFileSorter that = (RemoteFileSorter) o;
      //place folders first
      if (that.isDirectory() && !isDirectory()) {
        return 1;
      }
      if (!that.isDirectory() && isDirectory()) {
        return -1;
      }
      return fs[8].compareTo(that.fs[8]);
    }
  }

  public static void loadIcons(Class cls) {
    for (int a = 0; a < icons.length; a++) {
      try {
        InputStream is = cls.getClassLoader().getResourceAsStream(icons[a]);
        int len = is.available();
        byte data[] = new byte[len];
        is.read(data);
        is.close();
        ii[a] = new ImageIcon(data);
      } catch (Exception e) {
        JFLog.log("err:loadIcons() Failed:" + e);
      }
    }
  }

  public void init(String initLocalDir) {
    if (initLocalDir.length() > 0) {
      localDir.setText(initLocalDir.replaceAll("\\\\", "/"));
    }
    TableColumn tc;
    localFolders.setTag(localFolders.root, "/", "", "");
    localTree.setModel(localFolders.getTreeModel());

    remoteFolders.setTag(remoteFolders.root, "/", "", "");
    remoteTree.setModel(remoteFolders.getTreeModel());

    tc = new TableColumn(0, 50, tableCellRenderer, tableCellEditor);
    tc.setHeaderRenderer(tableCellRenderer);
    tc.setHeaderValue(new JLabel("file"));
    localFilesTableColumnModel.addColumn(tc);
    tc = new TableColumn(1, 50, tableCellRenderer, tableCellEditor);
    tc.setHeaderRenderer(tableCellRenderer);
    tc.setHeaderValue(new JLabel("size"));
    localFilesTableColumnModel.addColumn(tc);
    tc = new TableColumn(2, 50, tableCellRenderer, tableCellEditor);
    tc.setHeaderRenderer(tableCellRenderer);
    tc.setHeaderValue(new JLabel("date"));
    localFilesTableColumnModel.addColumn(tc);

    tc = new TableColumn(0, 50, tableCellRenderer, tableCellEditor);
    tc.setHeaderRenderer(tableCellRenderer);
    tc.setHeaderValue(new JLabel("file"));
    remoteFilesTableColumnModel.addColumn(tc);
    tc = new TableColumn(1, 50, tableCellRenderer, tableCellEditor);
    tc.setHeaderRenderer(tableCellRenderer);
    tc.setHeaderValue(new JLabel("size"));
    remoteFilesTableColumnModel.addColumn(tc);
    tc = new TableColumn(2, 50, tableCellRenderer, tableCellEditor);
    tc.setHeaderRenderer(tableCellRenderer);
    tc.setHeaderValue(new JLabel("date"));
    remoteFilesTableColumnModel.addColumn(tc);
    tc = new TableColumn(3, 50, tableCellRenderer, tableCellEditor);
    tc.setHeaderRenderer(tableCellRenderer);
    tc.setHeaderValue(new JLabel("permissions"));
    remoteFilesTableColumnModel.addColumn(tc);
    tc = new TableColumn(4, 50, tableCellRenderer, tableCellEditor);
    tc.setHeaderRenderer(tableCellRenderer);
    tc.setHeaderValue(new JLabel("owner/group"));
    remoteFilesTableColumnModel.addColumn(tc);

    localFilesTableModel.addColumn("file");
    localFilesTableModel.addColumn("size");
    localFilesTableModel.addColumn("date");
    localFiles.setModel(localFilesTableModel);
    localFiles.setColumnModel(localFilesTableColumnModel);
    localFiles.setShowGrid(false);

    remoteFilesTableModel.addColumn("file");
    remoteFilesTableModel.addColumn("size");
    remoteFilesTableModel.addColumn("date");
    remoteFilesTableModel.addColumn("permissions");
    remoteFilesTableModel.addColumn("owner/group");
    remoteFiles.setModel(remoteFilesTableModel);
    remoteFiles.setColumnModel(remoteFilesTableColumnModel);
    remoteFiles.setShowGrid(false);

    if (JF.isWindows()) {
      parseLocalRootWindows();
    }
    parseLocal();

    setState(true);
  }

  private void parseLocalRootWindows() {
    localFilesTableModel.setRowCount(0);
    for (char drv = 'a'; drv <= 'z'; drv++) {
      try {
        File test = new File("" + drv + ":\\");
        if (!test.exists()) {
          continue;
        }
        localFilesTableModel.addRow(new Object[]{new SiteFile("" + drv + ":", 0, PIC_FOLDER, ""), "", ""});
        XML.XMLTag tag = localFolders.addSetTag(localFolders.root, "" + drv + ":", "", "");
        localTree.expandPath(new TreePath(tag.getPath()));
      } catch (Exception e) {
      }
    }
    localTree.setSelectionPath(new TreePath(localFolders.root.getPath()));
  }

  public XML.XMLTag localTag;

  private void parseLocal() {
    String wd = localDir.getText().replaceAll("\\\\", "/");
    if (JF.isWindows() && wd.equals("/")) {
      parseLocalRootWindows();
      return;
    }
    localFilesTableModel.setRowCount(0);
    String wds[] = wd.split("/+");  //+=greedy
    //make sure all folders to current path are in tree
    localTag = localFolders.root;
    localTree.expandPath(new TreePath(localTag.getPath()));
    for (int a = 0; a < wds.length; a++) {
      if (wds[a].length() == 0) {
        continue;
      }
      localTag = localFolders.addSetTag(localTag, wds[a], "", "");
      localTree.expandPath(new TreePath(localTag.getPath()));
//      showPath("local", tag.getPath());
    }
    localTree.setSelectionPath(new TreePath(localTag.getPath()));
    if (!wd.equals("/")) {
      //add ".." entry
      localFilesTableModel.addRow(new Object[]{
        new SiteFile("..", 0, PIC_FOLDER, ""), "", ""
      });
    }
    try {
      File spec = new File(wd);
      File files[] = spec.listFiles();
      if ((files == null) || (files.length == 0)) {
        return;
      }
      LocalFileSorter sortedFiles[] = new LocalFileSorter[files.length];
      for (int idx = 0; idx < files.length; idx++) {
        sortedFiles[idx] = new LocalFileSorter(files[idx]);
      }
      Arrays.sort(sortedFiles);
      for (int idx = 0; idx < sortedFiles.length; idx++) {
        String fn = sortedFiles[idx].file.getName();
        int size = (int) sortedFiles[idx].file.length();
        Date lm = new Date(sortedFiles[idx].file.lastModified());
        String date = new SimpleDateFormat("yyyy-MM-dd kk:mm").format(lm);
        if (sortedFiles[idx].file.isDirectory()) {
          localFilesTableModel.addRow(new Object[]{
            new SiteFile(fn, 0, PIC_FOLDER, ""), "" + size, date
          });
          if (fn.equals("..")) {
            continue;
          }
          localFolders.addSetTag(localTag, fn, "", "");
        } else {
          localFilesTableModel.addRow(new Object[]{
            new SiteFile(fn, 0, PIC_FILE, ""), "" + size, date
          });
        }
      }
    } catch (Exception e) {
      setStatus("Error:" + e);
      JFLog.log(e);
      addLog("Error:" + e);
    }
  }

  public XML.XMLTag remoteTag;

  public void parseRemote(String wd, String ls) {
    //drwxr-xr-x    3 0        0            4096 Jan 03 20:48 ..
    //attr       node owner    group    filesize mth dy time  filename  //time=year if older than 1 year
//    ls = ls.replaceAll("\t", " ");
    String wds[] = wd.split("/+");  //+=greedy
    //make sure all folders to current path are in tree
    remoteTag = remoteFolders.root;
    remoteTree.expandPath(new TreePath(remoteTag.getPath()));
    for (int a = 0; a < wds.length; a++) {
      if (wds[a].length() == 0) {
        continue;
      }
      remoteTag = remoteFolders.addSetTag(remoteTag, wds[a], "", "");
      remoteTree.expandPath(new TreePath(remoteTag.getPath()));
//      showPath("remote", tag.getPath());
    }
    remoteTree.setSelectionPath(new TreePath(remoteTag.getPath()));
    String files[] = ls.replaceAll("\r", "").split("\n");
    RemoteFileSorter sortedFiles[] = new RemoteFileSorter[files.length];
    for (int a = 0; a < files.length; a++) {
//JFLog.log("files[]=" + files[a]);  //TEST
      if (files[a].length() == 0) {
        continue;
      }
      String fs[] = new String[10];
      for (int b = 0; b < 8; b++) {
        files[a] = files[a].trim();
        int idx = files[a].indexOf(" ");
        if (idx == -1) {
          idx = files[a].length();
        }
        fs[b] = files[a].substring(0, idx);
        files[a] = files[a].substring(fs[b].length() + 1);
      }
      fs[8] = files[a];  //what's left is filename
      if (fs[0].charAt(0) == 'l') {
        //parse link info
        int idx = fs[8].indexOf("->");
        if (idx == -1) {
          fs[9] = "?";
//          JFLog.log("unknown link:" + fs[8]);
        } else {
          fs[9] = fs[8].substring(idx + 3);
          fs[8] = fs[8].substring(0, idx - 1);
//          JFLog.log("link:" + fs[8] + " -> " + fs[9]);
        }
      } else {
        fs[9] = "";
      }
      sortedFiles[a] = new RemoteFileSorter(fs);
    }
    Arrays.sort(sortedFiles);
    remoteFilesTableModel.setRowCount(0);
    for (int a = 0; a < sortedFiles.length; a++) {
      if (sortedFiles[a] == null) {
        continue;
      }
      String fs[] = sortedFiles[a].fs;
      //fs[0] == attr
      //fs[1] == node
      //fs[2] == owner
      //fs[3] == group
      //fs[4] == filesize
      //fs[5] == month
      //fs[6] == day
      //fs[7] == time(HH:MM) or year
      //fs[8] == filename
      //fs[9] == link target (if link)
      if (fs[0].length() != 10) {
        continue;
      }
      if (fs[8].equals(".")) {
        continue;
      }
      if (fs[8].equals("..") && wd.equals("/")) {
        continue;
      }
      switch (fs[0].charAt(0)) {
        case 'd':
          //directory
          remoteFilesTableModel.addRow(new Object[]{
            new SiteFile(fs[8], 0, PIC_FOLDER, ""), "" + fs[4], "" + fs[5] + "," + fs[6] + " " + fs[7], "" + fs[0], "" + fs[2] + "/" + fs[3]
          });
          if (fs[8].equals("..")) {
            break;
          }
          remoteFolders.addSetTag(remoteTag, fs[8], "", "");
          break;
        case 'l':
          //link
          remoteFilesTableModel.addRow(new Object[]{
            new SiteFile(fs[8], 0, PIC_LINK, fs[9]), "" + fs[4], "" + fs[5] + "," + fs[6] + " " + fs[7], "" + fs[0], "" + fs[2] + "/" + fs[3]
          });
          break;
        default:
          remoteFilesTableModel.addRow(new Object[]{
            new SiteFile(fs[8], Integer.valueOf(fs[4]), PIC_FILE, ""), "" + fs[4], "" + fs[5] + "," + fs[6] + " " + fs[7], "" + fs[0], "" + fs[2] + "/" + fs[3]
          });
          break;
      }
    }
    progress.setValue(0);  //listing is actually a file transfer
  }

  public void local_chdir(String path) {
    String wd = localDir.getText();
    path = path.replaceAll("\\\\", "/");
    if (path.equals(".")) {
      //do nothing
    } else if (path.equals("..")) {
      //remove last path element
      int idx = wd.lastIndexOf('/');
      if (idx == -1) {
        wd = "/";
      } else {
        if (JF.isWindows() && wd.length() == 3) {
          wd = "/";
        } else {
          wd = wd.substring(0, idx);
        }
        if (JF.isWindows()) {
          if (wd.length() == 2) {
            wd += "/";
          }
        }
      }
    } else {
      if ((path.length() == 2) && (path.charAt(1) == ':')) {
        wd = path + "/";
      } else {
        if (path.indexOf("/") != -1) {
          //absolute path
          wd = path;
        } else {
          //add path element
          if (JF.isWindows() && wd.equals("/")) {
            wd = path + "/";
          } else {
            if (!wd.endsWith("/")) {
              wd += "/";
            }
            wd += path;
          }
        }
      }
    }
    localDir.setText(wd);
    parseLocal();
  }

  public void addLog(String msg) {
    String old = log.getText();
    if (old.length() == 0) {
      log.setText(msg);
    } else {
      log.setText(old + "\r\n" + msg);
    }
    log.setCaretPosition(log.getText().length() - 1);
  }

  public void setState(boolean state) {
    localDir.setEnabled(state);
    remoteDir.setEnabled(state);
    localTree.setEnabled(state);
    localFiles.setEnabled(state);
    remoteTree.setEnabled(state);
    remoteFiles.setEnabled(state);
    abort.setVisible(!state);
    progress.setValue(0);
  }

  public void setTotalFileSize(int value) {
//    System.out.println("setTotalFileSize:" + value);
    totalFileSize = value;
    setProgress(0);
  }

  private int progressPercent = 0;

  public void setProgress(int value) {
    float x = value;
    float y = totalFileSize;
    int newPercent = (int) (x / y * 100.0f);
    if (newPercent == progressPercent) return;
    progressPercent = newPercent;
//    System.out.println("setProgress:" + newPercent);
    java.awt.EventQueue.invokeLater(new Runnable() {
      public void run() {
        progress.setValue(progressPercent);
      }
    });
  }

  public void resize() {
    int w = this.getWidth();
    int h = this.getHeight();
    h -= status_pane.getPreferredSize().height;
    split_main.setDividerLocation(h);
    split_files.setDividerLocation(w / 2);
    split_local.setDividerLocation(h / 2);
    split_remote.setDividerLocation(h / 2);
  }

  public void local_mkdir() {
    String fn = JFAWT.getString("Enter new folder name", "");
    if ((fn == null) || (fn.length() == 0)) {
      return;
    }
    if (fn.indexOf(":") != -1) {
      return;
    }
    if (fn.indexOf("/") != -1) {
      return;
    }
    if (fn.indexOf("?") != -1) {
      return;
    }
    if (fn.indexOf("*") != -1) {
      return;
    }
    String wd = localDir.getText();
    if (!wd.endsWith("/")) {
      wd += "/";
    }
    try {
      local_mkdir(wd + fn);
      localFolders.addSetTag(localTag, fn, "", "");
      local_chdir(".");  //refresh
    } catch (Exception e) {
      JFAWT.showError("Error", "Create folder failed:" + e);
    }
  }

  public void local_mkdir(String path) {
    File file = new File(path);
    file.mkdir();
  }

  public void local_delete() {
    if (!JFAWT.showConfirm("Delete", "Are you sure you want to delete file(s)?")) {
      return;
    }
    aborted = false;
    localFolderStack = new ArrayList<String>();
    local_delete_files();
    local_chdir(".");  //refresh
  }

  private void local_delete_files() {
    int rows[] = localFiles.getSelectedRows();
    if (rows.length == 0) {
      return;
    }
    SiteFile sf[] = new SiteFile[rows.length];
    for (int idx = 0; idx < rows.length; idx++) {
      sf[idx] = (SiteFile) localFilesTableModel.getValueAt(rows[idx], 0);
    }
    for (int idx = 0; idx < sf.length; idx++) {
      if (sf[idx].getText().equals(".")) {
        continue;
      }
      if (sf[idx].getText().equals("..")) {
        continue;
      }
      totalFileSize = sf[idx].filesize;
      if (sf[idx].isDir) {
        localFolderStack.add(localDir.getText());
        local_chdir(sf[idx].getText());
      }
      if (sf[idx].isLink && !sf[idx].isDir) {
        //test if dir
        String d1 = localDir.getText();
        local_chdir(sf[idx].getText());
        String d2 = localDir.getText();
        if (!d1.equals(d2)) {
          localFolderStack.add(d1);
          sf[idx].isDir = true;
        }
      }
      if (sf[idx].isDir) {
        //already local_chdir
        localFiles.selectAll();
        local_delete_files();
        local_chdir(localFolderStack.remove(localFolderStack.size() - 1));
      }
      File localFile = new File(localDir.getText() + "/" + sf[idx].getText());
      local_delete_file(localFile);
      if (aborted) {
        break;
      }
    }
  }

  private void local_delete_file(File file) {
    try {
      file.delete();
      //update localTree
      for (int a = 0; a < localTag.getChildCount(); a++) {
        XML.XMLTag child = localTag.getChildAt(a);
        if (child.getName().equals(file.getName())) {
          localFolders.deleteTag(child);
          break;
        }
      }
    } catch (Exception e) {
      setStatus("Error:" + e);
      JFLog.log(e);
      addLog("Error:" + e);
    }
  }

  public void local_rename() {
    int rows[] = localFiles.getSelectedRows();
    if ((rows == null) || (rows.length != 1)) {
      return;
    }
    SiteFile sf = (SiteFile) localFilesTableModel.getValueAt(rows[0], 0);
    File localFile = new File(localDir.getText() + "/" + sf.getText());
    String newName = JFAWT.getString("Rename " + (localFile.isDirectory() ? "Folder" : "File"), sf.getText());
    if ((newName == null) || (newName.length() == 0)) {
      return;
    }
    File newFile = new File(localDir.getText() + "/" + newName);
    localFile.renameTo(newFile);
    //update localTree
    for (int a = 0; a < localTag.getChildCount(); a++) {
      XML.XMLTag child = localTag.getChildAt(a);
      if (child.getName().equals(sf.getText())) {
        child.setName(newName);
        break;
      }
    }
    local_chdir(".");  //refresh
  }

  public void setStatus(String str) {
    if (str == null) {
      setState(true);
      status.setText("Idle");
    } else {
      setState(false);
      status.setText(str);
    }
  }

  private void showPath(String name, Object path[]) {
    System.out.println(name);
    for (int a = 0; a < path.length; a++) {
      System.out.println("path[]=" + path[a]);
    }
  }

  private void dblclickLocal() {
    //dbl click local
    int rows[] = localFiles.getSelectedRows();
    if (rows.length != 1) {
      return;
    }
    SiteFile sf = (SiteFile) localFilesTableModel.getValueAt(rows[0], 0);
    if (!sf.isDir) {
      upload();
    } else {
      local_chdir(sf.getText());
    }
  }

  private void dblclickRemote() {
    //dbl click remote
    int rows[] = remoteFiles.getSelectedRows();
    if (rows.length != 1) {
      return;
    }
    SiteFile sf = (SiteFile) remoteFilesTableModel.getValueAt(rows[0], 0);
    if (sf.isLink && !sf.isDir) {
      //test if folder
      String d1 = remoteDir.getText();
      remote_chdir(sf.getText());
      String d2 = remoteDir.getText();
      if (!d1.equals(d2)) {
        sf.isDir = true;
        return;
      }
    }
    if (!sf.isDir) {
      download();
    } else {
      remote_chdir(sf.getText());
    }
  }

  private void dblclickLocalTree() {
    TreePath paths[] = localTree.getSelectionPaths();
    if (paths.length != 1) {
      return;
    }
    XML.XMLTag tag = localFolders.getTag(paths[0]);
    ArrayList<String> pathElements = new ArrayList<String>();
    do {
      if (tag.getName().equals("/")) {
        break;  //top node
      }
      pathElements.add(0, tag.getName());  //insert at top
      tag = tag.getParent();
    } while (tag != null);
    String fullPath = "";
    for (int a = 0; a < pathElements.size(); a++) {
      if (!((fullPath.length() == 0) && (JF.isWindows()))) {
        if (!fullPath.endsWith("/")) {
          fullPath += "/";
        }
      }
      fullPath += pathElements.get(a);
    }
    if (fullPath.equals("")) {
      fullPath = "/";
    }
    local_chdir(fullPath);
  }

  private void dblclickRemoteTree() {
    TreePath paths[] = remoteTree.getSelectionPaths();
    if (paths.length != 1) {
      return;
    }
    XML.XMLTag tag = remoteFolders.getTag(paths[0]);
    ArrayList<String> pathElements = new ArrayList<String>();
    do {
      if (tag.getName().equals("/")) {
        break;  //top node
      }
      pathElements.add(0, tag.getName());  //insert at top
      tag = tag.getParent();
    } while (tag != null);
    String fullPath = "";
    for (int a = 0; a < pathElements.size(); a++) {
      if (!fullPath.endsWith("/")) {
        fullPath += "/";
      }
      fullPath += pathElements.get(a);
    }
    if (fullPath.equals("")) {
      fullPath = "/";
    }
    remote_chdir(fullPath);
  }
}
